#ifndef __XVALUE_H__
#define __XVALUE_H__

#include "math_config.h"
#include "util/utils.h"
#include <map>
#include <memory>
#include "math/complex.hpp"
#include "math/array/array.h"
#include "math/value_helper.h"

NS_MATH_BEGIN

typedef void xval_nil;
typedef bool xval_bool;
typedef __int64 xval_int;
typedef double xval_float;
typedef complex<double> xval_complex;
typedef ::xphysics::util::xstring xval_string;


class XValueBase : public ::xphysics::util::ObjectData
{
public:
	virtual ::xphysics::util::ObjectData* Clone() const;

public:
	virtual ~XValueBase(){}

public:
	virtual int GetTypeID() const{return 0;}
};

// Default Template XValue Class
// T = bool, int, double, complex, ...
// T != void
template<typename T>
class XValueT : public XValueBase
{
public:
	typedef XValueT<T> this_type;
	typedef XValueBase base_type;
public:
	virtual ~XValueT(){}

public:
	virtual ::xphysics::util::ObjectData* Clone() const
	{
		this_type* ret = new this_type(*this);
		return dynamic_cast<::xphysics::util::ObjectData*>(ret);
	}

public:
	T& GetRef(){return val;}
	const T& GetRef()const{return val;}
	T* GetPtr(){return &val;}
	const T* GetPtr()const{return &val;}
	virtual int GetTypeID() const{return TypeInfoT<T>::id;}
protected:
	T val;
};

template<>
class XValueT<xobject> : public XValueBase
{
public:
	typedef XValueT<xobject> this_type;
	typedef XValueBase base_type;
public:
	virtual ~XValueT(){}

public:
	// Different Behavior of Clone()
	virtual ::xphysics::util::ObjectData* Clone() const
	{
		this_type* ret = new this_type();
		ret->val = this->val.Clone();
		return dynamic_cast<::xphysics::util::ObjectData*>(ret);
	}
public:
	xobject& GetRef(){return val;}
	const xobject& GetRef()const{return val;}
	xobject* GetPtr(){return &val;}
	const xobject* GetPtr()const{return &val;}

protected:
	xobject val;
};

class XValueHelper;

// Class XValue, a multiple-type-mapping class
class XValue : public ::xphysics::util::ObjectT<XValueBase>
{
public:
	typedef XValue this_type;
	typedef ::xphysics::util::ObjectT<XValueBase> base_type;
	friend class XValueHelper;
public:
	virtual ~XValue(){}
	XValue()
	{}
	XValue(XValue& src)
	{
		ptr = src.ptr;
	}

	XValue& operator=(XValue& src)
	{		
		if(this==&src)return *this;
		// Copy Ref
		ptr = src.ptr;
		return *this;
	}
public:
	XValue Clone() const
	{
		this_type ret;
		ret.ptr = dynamic_cast<XValueBase*>(ptr->Clone());
		return ret;
	}

public:
	// Type Error Info
	void type_error() const
	{
		::xphysics::util::Thread::this_logger().LogError("INVALID_TYPE");
		throw ::xphysics::util::exception("INVALID_TYPE");
	}

public:
	// Type Manipulation
	int GetTypeID()const
	{
		if(this->ptr==NULL)
		{
			return 0;
		}else
		{
			return this->ptr->GetTypeID();
		}
	}
	template<typename T>
	bool IsType() const
	{
		return this->GetTypeID()==TypeInfoT<T>::id;
	}
	bool IsType(int TypeID) const
	{
		return this->GetTypeID()==TypeID;
	}

	bool IsObject() const
	{
		switch(GetTypeID())
		{
		case TypeInfoT<xobject>::id:
			return true;
		default:
			return false;
		}
	}

	bool IsNumeric() const
	{
		switch(GetTypeID())
		{
		case TypeInfoT<int32>::id:
		case TypeInfoT<int64>::id:
		case TypeInfoT<float32>::id:
		case TypeInfoT<float64>::id:
		case TypeInfoT<complex32>::id:
		case TypeInfoT<complex64>::id:
			return true;
		default:
			return false;
		}
	}

	bool IsBoolean() const
	{
		return IsType<bool>();
	}

	bool IsNumericArray() const
	{
		switch(GetTypeID())
		{
		case TypeInfoT<arr_int32>::id:
		case TypeInfoT<arr_int64>::id:
		case TypeInfoT<arr_float32>::id:
		case TypeInfoT<arr_float64>::id:
		case TypeInfoT<arr_complex32>::id:
		case TypeInfoT<arr_complex64>::id:
			return true;
		default:
			return false;
		}
	}

	bool IsArray() const
	{
		switch(GetTypeID())
		{
		case TypeInfoT<arr_int32>::id:
		case TypeInfoT<arr_int64>::id:
		case TypeInfoT<arr_float32>::id:
		case TypeInfoT<arr_float64>::id:
		case TypeInfoT<arr_complex32>::id:
		case TypeInfoT<arr_complex64>::id:
		case TypeInfoT<arr_string>::id:
		case TypeInfoT<arr_object>::id:
			return true;
		default:
			return false;
		}
	}

	template<typename T>
	void SetType()
	{
		if(TypeInfoT<T>::id==TypeInfoT<void>::id)
		{
			this->ptr = NULL;
		}else if(!IsType<T>())
		{
			this->ptr = dynamic_cast<XValueBase*>(new XValueT<T>());
		}
	}
	void SetType(int TypeID)
	{
		if(TypeID!=GetTypeID())
		{
			switch(GetTypeID())
			{
			case TypeInfoT<void>::id:			SetNil();					break;
			case TypeInfoT<bool>::id:			SetType<bool>();			break;
			case TypeInfoT<int32>::id:			SetType<int32>();			break;
			case TypeInfoT<int64>::id:			SetType<int64>();			break;
			case TypeInfoT<float32>::id:		SetType<float32>();			break;
			case TypeInfoT<float64>::id:		SetType<float64>();			break;
			case TypeInfoT<complex32>::id:		SetType<complex32>();		break;
			case TypeInfoT<complex64>::id:		SetType<complex64>();		break;
			case TypeInfoT<arr_bool>::id:		SetType<arr_bool>();		break;
			case TypeInfoT<arr_int32>::id:		SetType<arr_int32>();		break;
			case TypeInfoT<arr_int64>::id:		SetType<arr_int64>();		break;
			case TypeInfoT<arr_float32>::id:	SetType<arr_float32>();		break;
			case TypeInfoT<arr_float64>::id:	SetType<arr_float64>();		break;
			case TypeInfoT<arr_complex32>::id:	SetType<arr_complex32>();	break;
			case TypeInfoT<arr_complex64>::id:	SetType<arr_complex64>();	break;
			case TypeInfoT<arr_string>::id:		SetType<arr_string>();		break;
			default: type_error();
			}
		}
	}

	// About Nil
	void SetNil()
	{
		this->ptr = NULL;
	}
	bool IsNil() const
	{
		return IsType<void>();
	}
	// Value Manipulation
	template<typename T>
	T GetValue()const
	{
		if(IsType<T>() && !IsNil())
		{
			const XValueT<T>* p = dynamic_cast<const XValueT<T>* >(this->ptr.get());
			return p->GetRef();
		}else
		{
			type_error();
		}
	}
	template<typename T>
	void SetValue(const T& val)
	{
		if(IsType<T>())
		{
			dynamic_cast<XValueT<T>* >(this->ptr.get())->GetRef() = val;
		}else
		{
			SetType<T>();
			dynamic_cast<XValueT<T>* >(this->ptr.get())->GetRef() = val;
		}
	}

	// Ref Manipulation
	template<typename T>
	T& GetRef()
	{
		if(IsType<T>())
		{
			return dynamic_cast<XValueT<T>* >(this->ptr.get())->GetRef();
		}else
		{
			type_error();
		}
	}

	template<typename T>
	const T& GetRef() const
	{
		if(IsType<T>())
		{
			return dynamic_cast<const XValueT<T>* >(this->ptr.get())->GetRef();
		}else
		{
			type_error();
		}
	}

	// Pointer Manipulation
	template<typename T>
	T* GetPtr()
	{
		if(IsType<T>())
		{
			return dynamic_cast<XValueT<T>* >(this->ptr.get())->GetPtr();
		}else
		{
			type_error();
		}
	}

	template<typename T>
	const T* GetPtr() const
	{
		if(IsType<T>())
		{
			return dynamic_cast<const XValueT<T>* >(this->ptr.get())->GetPtr();
		}else
		{
			type_error();
		}
	}

};




NS_MATH_END

#endif